package oceanserv

import (
	"config"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"ocean"
)

var (
	upgrader = websocket.Upgrader{ReadBufferSize: 1024,
		WriteBufferSize: 1024}
	gateways = make(map[string]*Gateway)
	myMux    = &myHttpServerMux{}
)

type myHttpServerMux struct {
}

func (mux *myHttpServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var path = r.URL.Path
	log.Println("path: ", path)
	if path == "/gnws" {
		acceptWebsocket(w, r)
		return
	} else {
		log.Printf("no handler for Request, path:%s\n", path)
		w.WriteHeader(404)
		return
	}
}

func CreateHttpServer() {
	blPortStr := fmt.Sprintf(":%d", config.OServerPort)
	s := &http.Server{
		Addr:    blPortStr,
		Handler: myMux,
		// ReadTimeout:    10 * time.Second,
		//WriteTimeout:   120 * time.Second,
		MaxHeaderBytes: 1 << 8,
	}

	go acceptRequest(s)
}

func acceptRequest(s *http.Server) {
	log.Printf("Http server listen at:%d\n", config.OServerPort)

	err := s.ListenAndServe()
	if err != nil {
		log.Println("Http server ListenAndServe failed:", err)
	}
}

func acceptWebsocket(w http.ResponseWriter, r *http.Request) {
	gwId := r.Header.Get("gwid")
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	if gwId == "" {
		log.Println("web socket has not token, close it")
		ws.Close()
		return
	}

	older, ok := gateways[gwId]
	if ok {
		log.Println("duplicate websocket, close old one")
		older.ws.Close()
		<-older.chanx
	}

	gateway := newGateway(gwId, ws)
	gateways[gwId] = gateway

	gateway.save2DB()

	log.Println("accept a websocket")

	for {
		messageType, binary, err := ws.ReadMessage()

		if err != nil {
			log.Println("websocket receive error:", err)
			onConnectionBreak(gwId)
			break
		}

		if messageType != websocket.BinaryMessage {
			log.Println("receive gateway text message:", string(binary))
			continue
		}

		if binary != nil && len(binary) > 0 {
			dispatchBinary(gwId, binary)
		}

	}

	ws.Close()

	gateway.chanx <- true
	close(gateway.chanx)
	delete(gateways, gwId)
}

func dispatchBinary(gwId string, binary []byte) {
	tunnel := &ocean.Tunnel{}
	err := proto.Unmarshal(binary, tunnel)
	if err != nil {
		log.Println("dispatch binary error:", err)
		return
	}

	//log.Println("ws got tunnel:", tunnel)

	if tunnel.GetTarget() == 0 || gwId == fmt.Sprintf("%d", int(tunnel.GetTarget())) {
		onGatewayMessage(gwId, tunnel)
		return
	}

	onControllerMessage(gwId, tunnel)
}

func onGatewayMessage(gwId string, tunnel *ocean.Tunnel) {
	g, ok := gateways[gwId]
	if !ok {
		log.Println("onGatewayMessage failed, no such gateway:", gwId)
		return
	}

	g.onGatewayMessage(tunnel)
}

func onControllerMessage(gwId string, tunnel *ocean.Tunnel) {
	g, ok := gateways[gwId]
	if !ok {
		log.Println("onControllerMessage failed, no such gatway:", gwId)
		return
	}

	g.onControllerMessage(tunnel)
}

func onConnectionBreak(gwId string) {
	g, ok := gateways[gwId]
	if !ok {
		return
	}

	g.onConnectionBreak()
}

func GetController(gwId string, controllerId int32) *Controller {
	g, ok := gateways[gwId]
	if !ok {
		log.Println("GetController failed, no such gateway:", gwId)
		return nil
	}

	c, ok := g.controllers[controllerId]
	if !ok {
		log.Println("GetController failed, no such controller:", controllerId)
		return nil
	}

	return c
}

func GetControllers(gwId string) []*Controller {
	g, ok := gateways[gwId]
	if !ok {
		log.Println("GetControllers failed, no such gateway:", gwId)
		return nil
	}

	if len(g.controllers) < 1 {
		return nil
	}

	cc := make([]*Controller, len(g.controllers))

	var i = 0
	for _, v := range g.controllers {
		cc[i] = v
		i++
	}

	return cc
}

func GetControllerById(controllerId int32) *Controller {
	for _, g := range gateways {
		controllers := g.controllers
		c, ok := controllers[controllerId]
		if ok {
			return c
		}
	}
	return nil
}
